##__docformat__ = "restructuredtext en"
# ******NOTICE***************
# two_myopic.py module by Shoibal Chakravarty
#
# You may copy and use this module as you see fit with no
# guarantee implied provided you keep this notice in all copies.
# *****END NOTICE************
"""
This module contains the functions that simulate a scenario
where one region has planning horizon of the current and the next period while
the other two are myopic. The region that plans ahead deploys its best response
taking into account 1. the myopic behavior of the other two regions in the current
period,  and 2. its myopic behavior in the next period.
"""

__author__ = "Shoibal Chakravarty (shoibalc@princeton.edu)"
__version__ = "0.1"
__date__ = "2011/August"
__license__ = "BSD"
__all__ = ['twomyopiconelookahead','oneperiodlookaheadupdate','intersectionregionmyopic']

import numpy as np
from utility import U3
from myopic import myopicupdate, regionupdate, worldupdate, cumulativecost
from flat2 import flatten
import scipy.optimize
from two_game import finalperiodupdate

def twomyopiconelookahead(r1,r2,r3,w,com,precision=0.5,init_grid_size=40):
    """
    Here r1 looks ahead one period but r2 and r3 are myopic.  For every period 
    except the last one,  the current period r2 and r3 solutions are calculated first. 
    The best response of r1 in the current period and next period (where r2 is 
    myopic) is then calculated. In the last period all regions are myopic.
    
    twomyopiconelookahead(r1,r2,r3,...):
    
    r1 looks ahead one period, r2 and r3 are myopic.
    """
    r1.descriptor  = 'One period look ahead'
    r2.descriptor = r3.descriptor = 'Myopic'
    print "\n\nIn this scenario %s looks ahead one period while %s  and %s are myopic\n"% (r1.name, r2.name, r3.name)
    for t in range(w.T-1):
#  handle the myopic regions  first
        r2.g[t], r2.u1[t], r2.c1[t], r2.m[t] = myopicupdate(r2,r1,r3,w,t)
        regionupdate(r2,t)
        r3.g[t], r3.u1[t], r3.c1[t], r3.m[t] = myopicupdate(r3,r1,r2,w,t)
        regionupdate(r3,t)
# one region two period look ahead
        oneperiodlookaheadupdate(r1,r2,r3,w,com,t,precision,init_grid_size)
        worldupdate(r1,r2,r3,w,com,t)
        print "\nPeriod", t+1,"\n", r1.name, "g", r1.g[t], "e", r1.e[t],"\n", r2.name, "g", r2.g[t], "e", r2.e[t],\
        "\n",r3.name, "g", r3.g[t], "e", r3.e[t], "\ncg", w.cg[t+1],"\n"
# the last period is still myopic
    t = w.T-1
    finalperiodupdate(r1,t)
    r2.g[t], r2.u1[t], r2.c1[t], r2.m[t] = myopicupdate(r2,r1,r3,w,t)
    regionupdate(r2,t)
    r3.g[t], r3.u1[t], r3.c1[t], r3.m[t] = myopicupdate(r3,r1,r2,w,t)
    regionupdate(r3,t)
    worldupdate(r1,r2,r3,w,com,t)
    print "\nPeriod", t+1,"\n", r1.name, "g", r1.g[t], "e", r1.e[t],"\n", r2.name, "g", r2.g[t], "e", r2.e[t],\
        "\n",r3.name, "g", r3.g[t], "e", r3.e[t], "\ncg", w.cg[t+1],"\n"
    cumulativecost(r1,r2,r3,w)


def oneperiodlookaheadupdate(ra,rb,rc,w,com,t,precision,init_grid_size):
    """
    Function oneperiodlookaheadupdate maximizes the sum of current period
    utility and the next period discounted utility after the other 
    two regions calculate their best myopic result. A brute force search
    with progressive finer mesh size is used to find the solution. Successive calls
    to intersectionregionmyopic are used till a required tolerance is reached.
    """
    maxa = min(ra.M[t]*ra.E[t+1],(ra.E[t+1] - ra.G[t]))
    mina = 1e-6
    D0 = 19
    D = D0
    EPS = EPSa = init_grid_size*D
    print ra.name,'Search region: min = 0.0,  max =', maxa
    flag = True
    ybest = np.nan
    num = False
    while EPS > precision and flag:
# initial run with  init_grid_size. Subsequent runs reduce it by a factor of 20
        if num:
            D = np.random.random_integers(D0+1,D0+5)
#        print D
        EPSa = EPSa/D
        num=True
        mina, maxa,ybest1 = intersectionregionmyopic(ra,rb,rc,w,com,t,mina,maxa,EPSa)
#        err_best = (ybest - ybest1)**2
#        if np.sqrt(err_best) <= 0.25*precision:
#            print "1. ATTAINED REQUIRED PRECISION"
#            flag = False
#            break
#        else:
#            ybest = ybest1
        EPS = EPSa = maxa - mina
    if flag:
        print "3. ATTAINED REQUIRED PRECISION"
        ga = 0.5*(mina + maxa)
    else:
        ga = ybest
    
    parama = \
    (rb.g[t]+rc.g[t], com.cg0, com.greenpar, ra.B[t], ra.B[t+1], ra.cn[t], ra.co[t], ra.cn[t+1], ra.co[t+1], ra.G[t],\
    rb.G[t]+rc.G[t], w.G[0], ra.e[t], ra.E[t+1], ra.e[t+1], ra.E[t+2], ra.M[t], ra.M[t+1], (1 - com.discount)**com.years, True)
    u, ra.g[t], ra.u1[t], ra.c1[t], ra.g2[t], ra.u2[t], ra.c2[t], ra.m[t] =  U3(ga,*parama)
    regionupdate(ra,t)


def intersectionregionmyopic(r1,r2,r3,w,com,t,mina,maxa,EPSa):
    """
    Function intersectionregionmyopic performs the brute force search using
    scipy.optimize.brute and returns the current best solution and upper 
    and lower bounds of the search domain.
    """
    lena = np.ceil((maxa - mina)/EPSa)
    EPSa = (maxa - mina)/lena
    eps = 1e-5
    Wa = 3
    slice_a = np.s_[mina: maxa + eps: EPSa,]
    mgrid_a = np.mgrid[slice_a[0]]
    parama = \
    (r2.g[t]+r3.g[t], com.cg0, com.greenpar, r1.B[t], r1.B[t+1], r1.cn[t], r1.co[t], r1.cn[t+1], r1.co[t+1], r1.G[t],\
    r2.G[t]+r3.G[t], w.G[0], r1.e[t], r1.E[t+1], r1.e[t+1], r1.E[t+2], r1.M[t], r1.M[t+1], (1 - com.discount)**com.years, False)
#    (ga0+gc0, Bb, cg0, cfnb, cfob, Gb0, Ga0+Gc0, G0, eb0, Eb0, eb1, Eb1, delta, alpha, False)
    ybest = scipy.optimize.brute(U3,slice_a,args=parama,full_output=False,finish=None)
    
    xa  = [int(round((ybest - mina)/EPSa))]
    
    xa1 = [xx - Wa > 0 and mgrid_a[xx - Wa].tolist() or mgrid_a[0].tolist() for xx in xa]
    xa2 = [xx + Wa < (lena - 1) and mgrid_a[xx + Wa].tolist() or mgrid_a[-1].tolist() for xx in xa]
#    print xa1,'\n',xb1,'\n',xc1,'\n',xa2,'\n',xb2,'\n',xc2,'\n'
    mina, maxa = min(flatten(xa1)),max(flatten(xa2))
#    print mina, maxa, minb, maxb
    print "Search region update: min", mina, "max", maxa, "best", ybest
#    if flag:
    return mina, maxa, ybest




